#include "../include/CloudiskServer.h"
#include "../include/CryptoUtil.h"
#include "../include/RabbitMQ.h"
#include "../MicroService/signup/signup.srpc.h"
#include "../MicroService/signin/signin.srpc.h"

#include <nlohmann/json.hpp>
#include <workflow/MySQLResult.h>
#include <wfrest/PathUtil.h>
#include "workflow/WFTaskFactory.h"
#include "workflow/HttpUtil.h"


#include <vector>

using namespace wfrest;
using namespace protocol;
using std::vector;
using std::cout;
using std::endl;
using json = nlohmann::json;
using namespace srpc;

void CloudiskServer::register_modules() {
    // 设置静态资源的路由
    register_static_resources_module();
    // 注册 
    register_signup_module();
    // 登录
    register_signin_module();
    // 获取用户信息
    register_userinfo_module();
    // 文件列表查询
    register_filelist_module();
    // 上传
    register_fileupload_module();
    // 下载
    register_filedownload_module();
}

// 设置静态资源的路由
void CloudiskServer::register_static_resources_module() {
    m_server.GET("/user/signup", [](const HttpReq *, HttpResp * resp){
        resp->File("static/view/signup.html");
    });

    m_server.GET("/user/signin", [](const HttpReq *, HttpResp * resp){
        resp->File("static/view/signin.html");
    });

    m_server.GET("/static/view/signin.html", [](const HttpReq *, HttpResp * resp){
        resp->File("static/view/signin.html");
    });

    m_server.GET("/static/view/home.html", [](const HttpReq *, HttpResp * resp){
        resp->File("static/view/home.html");
    });

    m_server.GET("/static/js/auth.js", [](const HttpReq *, HttpResp * resp){
        resp->File("static/js/auth.js");
    });

    m_server.GET("/static/img/avatar.jpeg", [](const HttpReq *, HttpResp * resp){
        resp->File("static/img/avatar.jpeg");
    });

    m_server.GET("/file/upload", [](const HttpReq *, HttpResp * resp){
        resp->File("static/view/index.html");
    });

    m_server.Static("/file/upload_files","static/view/upload_files");
}

struct SignupCallbackNeed {
    HttpResp * resp;
    string username;
};

static void signup_done(SignupResponse *response, srpc::RPCContext *context)
{
    // 获得响应报文
    SignupCallbackNeed * scn = (SignupCallbackNeed*)context->get_user_data(); 

    bool success = response->success();
    if (!success) {
        string error = response->message();
        cout << "[error]: " << error << endl<< endl;
        scn->resp->String(error);
        return;
    }
    else {
        printf("新用户，");
        int ret = mkdir(("./file/" + scn->username).c_str(), 0777);
        if(ret == -1) {
            printf("文件空间创建失败!\n");
        }
        printf("已注册, 请使用!\n");
        printf("\n");

        // 返回SUCCESS
        scn->resp->String("SUCCESS");
    }
}

// 注册 
void CloudiskServer::register_signup_module(){
    m_server.POST("/user/signup", [](const HttpReq * req, HttpResp * resp) {
        // 先看req对不对
        if (req->content_type() != APPLICATION_URLENCODED) {
            resp->set_status(HttpStatusBadRequest); // Response Status: 400
            return ;
        }

        cout << "\nIn register_signup_module POST /user/signup: \n";

        // 提取用户名和密码
        auto & content = req->form_kv();
        string username = content["username"];
        string password = content["password"];

        cout << "[username]: " << username << "   password: " << password << endl;


        // 需要httptask访问界面  访问consul
        // 解析json
        // 得到 ip port
        string ip;
        unsigned short port;

        WFHttpTask* WFtask = WFTaskFactory::create_http_task(
            "http://192.168.142.128:8500/v1/health/service/UserService?passing=true", 
            3, 
            3,
            [&ip, &port] (WFHttpTask* task) {
                string jsonString = protocol::HttpUtil::decode_chunked_body(task->get_resp());

                // const void * body;
                // size_t size;
                // task->get_resp()->get_parsed_body(&body, &size);

                // cout << endl << endl << (const char *)body << endl << endl;

                // string jsonString {(const char *)body};

                // 解析 JSON
                json j = json::parse(jsonString);

                // 遍历所有服务
                for (const auto& service_entry : j) {
                    const auto& service = service_entry["Service"];
                    if (service["ID"] == "signup") {
                        ip = service["Address"];
                        port = service["Port"];
                        break;  // 找到后退出循环
                    }
                }
            }
        );

        HttpRequest * reqq = WFtask->get_req();
        reqq->set_method("GET");

        WFFacilities::WaitGroup waitGroup1 { 1 };
        SeriesWork* series1 = Workflow::create_series_work(WFtask, [&waitGroup1](const SeriesWork*)
        {
            waitGroup1.done();    
        });
        
        series1->start();
        waitGroup1.wait();

        cout << "[ip]: " << ip << "   [port]: " << port << endl;

        // INPUT： username password （user_data = resp）
        // OUTPUT： 

        // 创建SRPC的客户端
        Signup::SRPCClient client{ ip.c_str(), port };
    
        // 创建SRPCClientTask
        SRPCClientTask* signupTask = client.create_Signup_task(signup_done);
    
        // 设置请求
        SignupRequest signup_req;
        signup_req.set_username(username);
        signup_req.set_password(password);
        signupTask->serialize_input(&signup_req); /* serialize_input 设置请求 */
        signupTask->user_data = new SignupCallbackNeed{resp, username}; // 回调函数需要直接发响应报文

        WFFacilities::WaitGroup waitGroup { 1 };
        SeriesWork* series = Workflow::create_series_work(signupTask, [&waitGroup](const SeriesWork*)
        {
            waitGroup.done();    
        });
    
        series->start();
        waitGroup.wait();
    });
}

struct SigninCallbackNeed {
    HttpResp * resp;
    string username;
    string password;
};

static void signin_done(SigninResponse *response, srpc::RPCContext *context)
{
    // 获得响应报文
    SigninCallbackNeed * scn = (SigninCallbackNeed*)context->get_user_data(); 

    bool success = response->success();
    if (!success) {
        string error = response->message();
        cout << "[error]: " << error << endl<< endl;
        scn->resp->String(error);
        return;
    }
    else {
        string jsonString = response->message();
        json j = json::parse(jsonString);
        cout << "[json]:\n" << j.dump(2) << endl;
        // 返回SUCCESS
        scn->resp->String(jsonString);
    }

    cout << endl;
}

// 登录
void CloudiskServer::register_signin_module() {
    m_server.POST("/user/signin", [](const HttpReq * req, HttpResp * resp) {
        // 先看req对不对
        if (req->content_type() != APPLICATION_URLENCODED) {
            resp->set_status(HttpStatusBadRequest); // Response Status: 400
            return ;
        }

        cout << "\nIn register_signin_module POST /user/signin: \n";

        // 提取用户名和密码
        auto & content = req->form_kv();
        string username = content["username"];
        string password = content["password"];

        cout << "[username]: " << username << "   password: " << password << endl;



        // 需要httptask访问界面
        // 解析json
        // 得到 ip port
        string ip;
        unsigned short port;

        WFHttpTask* WFtask = WFTaskFactory::create_http_task(
            "http://192.168.142.128:8500/v1/health/service/UserService?passing=true", 
            3, 
            3,
            [&ip, &port] (WFHttpTask* task) {
                string jsonString = protocol::HttpUtil::decode_chunked_body(task->get_resp());

                // 解析 JSON
                json j = json::parse(jsonString);

                // 遍历所有服务
                for (const auto& service_entry : j) {
                    const auto& service = service_entry["Service"];
                    if (service["ID"] == "signin") {
                        ip = service["Address"];
                        port = service["Port"];
                        break;  // 找到后退出循环
                    }
                }
            }
        );

        HttpRequest * reqq = WFtask->get_req();
        reqq->set_method("GET");

        WFFacilities::WaitGroup waitGroup1 { 1 };
        SeriesWork* series1 = Workflow::create_series_work(WFtask, [&waitGroup1](const SeriesWork*)
        {
            waitGroup1.done();    
        });
        
        series1->start();
        waitGroup1.wait();

        cout << "[ip]: " << ip << "   [port]: " << port << endl;


        // INPUT： username password （user_data = resp）
        // OUTPUT： 

        // 创建SRPC的客户端
        Signin::SRPCClient client{ ip.c_str(), port };
    
        // 创建SRPCClientTask
        SRPCClientTask* signinTask = client.create_Signin_task(signin_done);
    
        // 设置请求
        SigninRequest signin_req;
        signin_req.set_username(username);
        signin_req.set_password(password);
        signinTask->serialize_input(&signin_req); /* serialize_input 设置请求 */
        signinTask->user_data = new SigninCallbackNeed{resp, username, password}; // 回调函数需要直接发响应报文

        WFFacilities::WaitGroup waitGroup { 1 };
        SeriesWork* series = Workflow::create_series_work(signinTask, [&waitGroup](const SeriesWork*)
        {
            waitGroup.done();    
        });
    
        series->start();
        waitGroup.wait();
    });
}

// 获取用户信息
void CloudiskServer::register_userinfo_module() {
    m_server.GET("/user/info", [](const HttpReq * req, HttpResp * resp) {
        cout << "\nIn register_userinfo_module GET /user/info: \n";
        
        // 查询 username 和 token
        const std::string& username = req->query("username");
        string username_p = username;
        const std::string& token = req->query("token");

        cout << "[username]: " << username_p << "  [token]: " << token << endl;

        // 校验token
        if (!CryptoUtil::verify_token(token, username_p)) {
            resp->String("userinfo token error");
            cout << "token error!" << endl;
            return;
        }

        // 根据用户名查询注册时间
        string murl = "mysql://root@localhost/cpp65";
        string msql = "SELECT created_at FROM tbl_user WHERE tomb = 0 AND username = '" + username + "'";
        
        cout << "[MYSQL] " << msql << endl;

        resp->MySQL(murl, msql, [resp, username_p](MySQLResultCursor* cursor){
            if (cursor->get_cursor_status() == MYSQL_STATUS_GET_RESULT) {
                vector<MySQLCell> record;
                string created_at;
                if (cursor->fetch_row(record)) {
                    created_at = record[0].as_string();

                    cout << "[created_at]: " << created_at << endl;

                    // 返回JSON
                    json j;
                    j["data"]["Username"] = username_p;
                    j["data"]["SignupAt"] = created_at;
                    resp->String(j.dump());

                    cout << "[json]:\n" << j.dump(2) << endl;

                    cout << endl;
                }
                else {
                    resp->String("userinfo user not found");
                }
            }
            else {
                resp->String("userinfo user not found");
            }
        });
    });
}

// 文件列表查询
void CloudiskServer::register_filelist_module() {
    m_server.POST("/file/query", [](const HttpReq * req, HttpResp * resp) {
        cout << "\nIn register_filelist_module POST /file/query: \n";
        
        // 查询 username 和 token
        const std::string& username = req->query("username");
        string username_p = username;
        const std::string& token = req->query("token");

        cout << "[username]: " << username_p << "  [token]: " << token << endl;

        // 提取limit
        auto & content = req->form_kv();
        int limit = stoi(content["limit"]);

        // 校验token
        if (!CryptoUtil::verify_token(token, username_p)) {
            resp->String("filelist token error");
            cout << "token error!" << endl;
            return;
        }

        // 根据用户名返回文件列表(limit限制长度)
        // 先根据用户名在 用户表 查其id
        // 再根据id作为uid在 文件表 查相关文件
        // 得到每一个文件的 filename hashcode size created_at last_update
        string murl1 = "mysql://root@localhost/cpp65";
        string msql1 = "SELECT id FROM tbl_user WHERE tomb = 0 AND username = '" + username_p + "'";
        
        cout << "[MYSQL] " << msql1 << endl;

        resp->MySQL(murl1, msql1, [resp, username_p, limit](MySQLResultCursor* cursor) {
            if (cursor->get_cursor_status() == MYSQL_STATUS_GET_RESULT) {
                vector<MySQLCell> record;
                int id;
                if (cursor->fetch_row(record)) {
                    id = record[0].as_int();

                    cout << "[id]: " << id << endl;

                    // 再根据id作为uid在 文件表 查相关文件
                    string murl2 = "mysql://root@localhost/cpp65";
                    string msql2 = string() + "SELECT filename, hashcode, size, created_at, last_update " +
                                   "FROM tbl_file WHERE status = 0 AND uid = '" + std::to_string(id) + "'";
                    
                    cout << "[MYSQL] " << msql2 << endl;

                    resp->MySQL(murl2, msql2, [resp, limit](MySQLResultCursor* cursor) { 
                        if (cursor->get_cursor_status() == MYSQL_STATUS_GET_RESULT) {
                            vector<MySQLCell> record;
                            // 得到每一个文件的 filename hashcode size created_at last_update
                            string filename, hashcode, created_at, last_update;
                            int size;
                            json j = json::array();
                            for (int i = 0; i < limit; ++i) {
                                if (cursor->fetch_row(record)) {
                                    filename = record[0].as_string();
                                    hashcode = record[1].as_string();
                                    size = record[2].as_int();
                                    created_at = record[3].as_string();
                                    last_update = record[4].as_string();
    
                                    cout << "[filename]: " << filename
                                         << "  [hashcode]: " << hashcode
                                         << "  [size]: " << size
                                         << "  [created_at]: " << created_at
                                         << "  [last_update]: " << last_update << endl;
                                    
                                    // 将这些内容写入json，发回去
                                    json j_temp;
                                    j_temp["FileName"] = filename;
                                    j_temp["FileHash"] = hashcode;
                                    j_temp["FileSize"] = size;
                                    j_temp["UploadAt"] = created_at;
                                    j_temp["LastUpdated"] = last_update;
                                    j.push_back(j_temp);

                                    cout << j_temp.dump(2) << endl;
                                }
                                else {
                                    break;
                                }
                            }

                            resp->String(j.dump());

                            cout << endl;
                        }
                    });
                }
                else {
                    resp->String("filelist user not found");
                }
            }
            else {
                resp->String("filelist user not found");
            }
        });
    });
}

#include <openssl/evp.h>
string CloudiskServer::sha256_hash(const char *data) {
    EVP_MD_CTX *ctx = EVP_MD_CTX_new();
    unsigned char hash[EVP_MAX_MD_SIZE];
    unsigned int hash_len;  // SHA_256, 256位二进制，用长为64的十六进制字符串表示
 
    EVP_DigestInit_ex(ctx, EVP_sha256(), NULL);
    EVP_DigestUpdate(ctx, data, strlen(data));
    EVP_DigestFinal_ex(ctx, hash, &hash_len);

    string result;
    result.reserve(hash_len * 2);  // 预分配空间（每个字节对应 2 个十六进制字符）
    for (unsigned int i = 0; i < hash_len; i++) {
        char buf[3];
        sprintf(buf, "%02x", hash[i]);
        result += buf;
    }

    EVP_MD_CTX_free(ctx);
    return result;
}

// 上传
void CloudiskServer::register_fileupload_module() {
    m_server.POST("/file/upload", [this](const HttpReq * req, HttpResp * resp) {
        // 先看req对不对
        if (req->content_type() != MULTIPART_FORM_DATA) {
            resp->set_status(HttpStatusBadRequest); // Response Status: 400
            return ;
        }

        cout << "\nIn register_fileupload_module POST /file/upload: \n";

        // 提取username
        const std::string& username = req->query("username");
        string username_p = username;

        // from()提取文件， 生成hash值
        const Form& form = req->form();
        string filePath = "./file/" + username_p;

        auto& [filename, content] = form.begin()->second;
        string filename_p = PathUtil::base(filename);
        string savepath = filePath + "/" + filename_p;
        int size = content.length();
        string hashcode = sha256_hash(content.c_str());
        
        cout << "[savepath]: " << savepath << "  [hashcode]: " << hashcode << endl;

        // 将文件信息写入数据库
        // 先根据用户名在 用户表 查 id，作为文件表uid
        // 再将 uid filename hashcode size 写入文件表
        string murl1 = "mysql://root@localhost/cpp65";
        string msql1 = "SELECT id FROM tbl_user WHERE tomb = 0 AND username = '" + username_p + "'";

        cout << "[MYSQL] " << msql1 << endl;

        resp->MySQL(murl1, msql1, [resp, filename_p, hashcode, size, savepath, &content](MySQLResultCursor* cursor) {
            if (cursor->get_cursor_status() == MYSQL_STATUS_GET_RESULT) {
                vector<MySQLCell> record;
                int id;
                if (cursor->fetch_row(record)) {
                    id = record[0].as_int();

                    cout << "[id]: " << id << endl;

                    // 用 uid 和 filename_p 查文件表
                    // 同一个人 的 相同名字文件存在，更新
                    //            不同名字，插入
                    string murl3 = "mysql://root@localhost/cpp65";
                    string msql3 = "SELECT id FROM tbl_file WHERE uid = '" + 
                                    std::to_string(id) + "' AND filename = '" +
                                    filename_p + "'";
                                    
                    cout << "[MYSQL] " << msql3 << endl;

                    // 如果有相同，则执行更新语句
                    // 没相同，则执行插入语句
                    resp->MySQL(murl3, msql3, [resp, id, filename_p, hashcode, size, savepath, &content](MySQLResultCursor* cursor) {
                        if (cursor->get_cursor_status() == MYSQL_STATUS_GET_RESULT) {
                            if (cursor->get_rows_count() > 0) { // 相同名字文件存在，更新
                                string murl4 = "mysql://root@localhost/cpp65";
                                string msql4 = "UPDATE tbl_file SET hashcode = '" +
                                                hashcode + "', size = " +
                                                std::to_string(size) + " WHERE uid = '" +
                                                std::to_string(id) + "' AND filename = '" +
                                                filename_p + "'";
                                cout << "[MYSQL] " << msql4 << endl;

                                resp->MySQL(murl4, msql4, [resp, savepath, &content](MySQLResultCursor* cursor) { 
                                    if (cursor->get_cursor_status() == MYSQL_STATUS_OK) {
                                        // string content_p = content;
                                        // Backup::backupByMemory(std::move(content_p), savepath);
                                        json j;
                                        j["srcpath"] = savepath;
                                        j["savepath"] = savepath;
                                        RabbitMQ::push("my.direct", "key1", j.dump());

                                        remove(savepath.c_str());
                                        resp->Save(savepath, std::move(content));
                                        resp->File("static/view/home.html");
                                    }
                                    else {
                                        resp->String("upload file update mysql_file error");
                                    }

                                    cout << endl;
                                });
                            }
                            else { // 没有相同，是不同名字，插入
                                string murl2 = "mysql://root@localhost/cpp65";
                                string msql2 = "INSERT INTO tbl_file (uid, filename, hashcode, size) VALUES (" +
                                                std::to_string(id) + ", '" +
                                                filename_p + "', '" +
                                                hashcode + "', " +
                                                std::to_string(size) + ")";
                                cout << "[MYSQL] " << msql2 << endl;

                                resp->MySQL(murl2, msql2, [resp, savepath, &content](MySQLResultCursor* cursor) { 
                                    if (cursor->get_cursor_status() == MYSQL_STATUS_OK) {
                                        // string content_p = content;
                                        // Backup::backupByMemory(std::move(content_p), savepath);
                                        json j;
                                        j["srcpath"] = savepath;
                                        j["savepath"] = savepath;
                                        RabbitMQ::push("my.direct", "key1", j.dump());

                                        resp->Save(savepath, std::move(content));
                                        resp->File("static/view/home.html");
                                    }
                                    else {
                                        resp->String("upload file insert into mysql_file error");
                                    }

                                    cout << endl;
                                });
                            }
                        }
                        else {
                            resp->String("upload file select isSame mysql_file error");
                        }

                    });
                }
                else {
                    resp->String("upload file error: no fetch");
                }
            }
            else {
                resp->String("upload file error: no user");
            }
        });
    });
}

// 下载
void CloudiskServer::register_filedownload_module() {
    m_server.GET("/file/download", [this](const HttpReq * req, HttpResp * resp) {
        cout << "\nIn register_filedownload_module GET /file/download: \n";

        // 提取 filename filehash username token
        const std::string& filename = req->query("filename");
        const std::string& filehash = req->query("filehash");
        const std::string& username = req->query("username");
        string username_p = username;
        const std::string& token = req->query("token");

        cout << "[filename]: " << filename << "  [filehash]: " << filehash
             << "  [username]: " << username_p << "  [token]: " << token << endl;

        // 校验token
        if (!CryptoUtil::verify_token(token, username_p)) {
            resp->String("filedownload token error");
            cout << "token error!" << endl;
            return;
        }

        // 返回文件
        string filePath = "./file/" + username_p + "/" + PathUtil::base(filename);
        resp->File(filePath);

        cout << endl;
    });
}

